package com.ys.erp.framework.handler.check;

import com.ys.erp.common.enums.LockEnum;
import com.ys.erp.framework.annotation.RedissonLock;
import com.ys.erp.framework.exception.YsException;
import com.ys.erp.framework.response.ResponseDTO;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.text.MessageFormat;

/**
 * @author minamiyama
 * @date 2025/5/15 14:58
 */
@Aspect
@Component
@RequiredArgsConstructor
public class RedissonLockAspect {

    private final RedissonClient redissonClient;
    private final ExpressionParser parser = new SpelExpressionParser();
    private final ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

    @Around(value = "@annotation(redissonLock)", argNames = "joinPoint,redissonLock")
    public Object around(ProceedingJoinPoint joinPoint, RedissonLock redissonLock) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        String[] paramNames = nameDiscoverer.getParameterNames(method);
        Class<?> declaringClass = signature.getDeclaringType();
        String className = declaringClass.getName();
        EvaluationContext context = new StandardEvaluationContext();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }
        // 解析 SpEL 表达式生成 key
        Expression expression = parser.parseExpression(redissonLock.key());
        String lockKey = expression.getValue(context, String.class);
        RLock lock = redissonClient.getLock(MessageFormat.format(LockEnum.LOCK.getLockKey(), className, method.getName(), lockKey));
        boolean acquired = lock.tryLock();
        if (!acquired) {
            throw new YsException(new ResponseDTO<>(-1, "请勿重复提交", null));
        }
        try {
            return joinPoint.proceed();
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
    }
}